package com.igrid.demo.client;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.SimplePanel;

/**
 * The Grid header cell widget,that can only contains one widget as his child.
 * 
 * <p>
 * The widget will fire resize and drag event
 * </p>
 * @author cuij
 * 
 */
public class HeaderCell extends SimplePanel implements ResizeEventSource,DragAndDropEventSource {

	//Class properties
	private static final int EDGE_TOLERANCE = 5;
	
	private static final int MINIMUM_TOLERANCE=10;
	//Set this to 60px for now
	private static final String RESIZE_BAR_HEIGHT="60px";
	//The css for floatGhostTh property
	private static final String STYLE_GHOST_TH="ghostTh";
	//The cell is in normal state
	private static final int NORMAL=0;
	//The cell is onDragging state
	private static final int ON_DRAG=1;
	//The cell is onResizing state
	private static final int ON_RESIZE=2;
	//Protected properties
	protected String text;
	//Private properties
	private ResizeHandler resizeHandler;
	
	private DragAndDropHandler dndHandler;
	//The current cell state{NORMAL,ON_DRAG,ON_RESIZE}
	private int state;
	
	private String alignment="center";
	
	private int eventStartX;
	
	private IGridHeader header;
	
	//A  boundary bar proxy;
	private PopupPanel floatBoundaryBar;
	
	//A  boundary bar proxy;
	private PopupPanel frozenBoundaryBar;
	//A th element proxy
	private PopupPanel floatGhostTh;
	
	
	//<p>Constructor</p>
	/**
	 * New HeaderCell instance with given text and parent
	 * 
	 */
	public HeaderCell(String text,String alignment,IGridHeader header) {
		assert header!=null:"Header is null";
		this.text=text;
		this.alignment=alignment;
		Label lb=new Label(this.text);
		lb.getElement().setPropertyString("align", this.alignment);
		this.add(lb);
		//DOM.setInnerHTML(this.getElement(), text);
		this.header=header;
		//Set the initial status to normal 
		state=NORMAL;
		
		addResizeHandler(this.header);
		addDragAndDropHandler(this.header);
		//Sink Events
		this.sinkEvents(Event.ONMOUSEMOVE | Event.ONMOUSEOVER
				| Event.ONMOUSEDOWN | Event.ONMOUSEUP);
	}
	
	//Public Methods
	// ResizeEventSource Interface
	/**
	 * Add a {@code ResizeHandler} instance
	 * 
	 */
	public void addResizeHandler(ResizeHandler handler) {
		this.resizeHandler = handler;

	}
	
	/**
	 * Remove a {@code ResizeHandler}
	 * 
	 */
	public void removeResizeHandler(ResizeHandler handler) {
		if (this.resizeHandler != handler) {
			throw new IllegalArgumentException(
					"Try to remove a not existing handler,Not Good!");
		} else {
			this.resizeHandler = null;
		}
	}
	
	//DragAndDropEventSource Interface
	public void addDragAndDropHandler(DragAndDropHandler handler) {
		this.dndHandler=handler;
	}

	public void removeDragAndDropHandler(DragAndDropHandler handler) {
		if (this.dndHandler != handler) {
			throw new IllegalArgumentException(
					"Try to remove a not existing handler,Not Good!");
		} else {
			this.dndHandler = null;
		}
	}
	
	
	public void onBrowserEvent(Event event) {
		// fire browser Native event;
		super.onBrowserEvent(event);
		switch (DOM.eventGetType(event)) {
			case Event.ONMOUSEMOVE:
				//If the cursor is at the most right edge ,then change the 
				//cursor style to e-resize,if it has not being dragged or resized.
				if (state == NORMAL) {
					setCursorStyle(event);
				}
				//If the cell is being resized,then positioned the proxy element with mouse moving
				if (state == ON_RESIZE) {
					int absoluteLeft= event.getClientX()+this.header.getTotalScrollLeft();
					int absoluteTop=this.getParent().getAbsoluteTop();
					if(absoluteLeft-this.frozenBoundaryBar.getAbsoluteLeft()>MINIMUM_TOLERANCE){
						setPopupProxyPosition(this.floatBoundaryBar,absoluteLeft,absoluteTop);
					}
				}
				//If the cell is being dragged,then positioned the proxy element with mouse moving
				if (state == ON_DRAG) {
					int absoluteLeft= event.getClientX()+this.header.getTotalScrollLeft();
					int absoluteTop=this.getParent().getAbsoluteTop();
					setPopupProxyPosition(this.floatGhostTh,absoluteLeft,absoluteTop);
					DOM.setStyleAttribute(this.getElement(), "cursor", "pointer");
					int offSetX=event.getClientX()-this.eventStartX;
					this.dndHandler.onDrag(this, event, offSetX);
				}
				break;
			case Event.ONMOUSEOVER:
				setCursorStyle(event);
				break;
			case Event.ONMOUSEDOWN:
				eventStartX=event.getClientX();
				Event.setCapture(this.getElement());
				if(atMostRight(event)){
					state = ON_RESIZE;
					this.initBoundarybar();
					this.floatBoundaryBar.show();
					this.frozenBoundaryBar.show();
					int absoluteLeft= event.getClientX()+this.header.getTotalScrollLeft();
					int absoluteTop=this.getParent().getAbsoluteTop();
					//Set the float boundary bar position
					setPopupProxyPosition(this.floatBoundaryBar,absoluteLeft,absoluteTop);
					//Set the frozen boundary bar position,
					//it should always stick to the most left edge when performing the resizing.
					setPopupProxyPosition(this.frozenBoundaryBar,this.getAbsoluteLeft()-4,absoluteTop);
				}else{
					state = ON_DRAG;
					this.initGhostTh();
					this.floatGhostTh.show();
					int absoluteLeft= this.getAbsoluteLeft();
					int absoluteTop=this.getParent().getAbsoluteTop();
					//Set the proxy Th elemnt position.
					setPopupProxyPosition(floatGhostTh,absoluteLeft,absoluteTop);
				}
				break;
			case Event.ONMOUSEUP:
				int offSetX=this.eventStartX-event.getClientX();
				if (state == ON_RESIZE) {
					this.releaseBoundaryBar();
					//fire Handlers
					this.resizeHandler.onResize(this, event,offSetX);
				}
				if (state == ON_DRAG) {
					this.releaseGhostTh();
					//fire Handlers
					this.dndHandler.onDrop(this, event, offSetX);
				}
				state=NORMAL;
				Event.releaseCapture(this.getElement());
				break;
		}
	}
	
	//Protected Methods
	
	protected void initBoundarybar(){
		PopupPanel t1 = new PopupPanel();
		DOM.setStyleAttribute(t1.getElement(), "width", "1px");
		DOM.setStyleAttribute(t1.getElement(), "height", RESIZE_BAR_HEIGHT);
		DOM.setStyleAttribute(t1.getElement(), "background", "pink");
		this.floatBoundaryBar = t1;
		PopupPanel t2 = new PopupPanel();
		DOM.setStyleAttribute(t2.getElement(), "width", "1px");
		DOM.setStyleAttribute(t2.getElement(), "height", RESIZE_BAR_HEIGHT);
		DOM.setStyleAttribute(t2.getElement(), "background", "pink");
		this.frozenBoundaryBar = t2;	
	}
	
	protected void initGhostTh() {
		PopupPanel gTh=new PopupPanel();
		Label lb=new Label(text);
		lb.setStyleName("text");
		lb.getElement().setPropertyString("align",this.alignment);
		gTh.add(lb);
		gTh.setWidth(this.getOffsetWidth()+"px");
		gTh.setHeight(this.getParent().getOffsetHeight()+"px");
		this.floatGhostTh=gTh;
		this.floatGhostTh.setStyleName(STYLE_GHOST_TH);
	}
	
	protected void releaseBoundaryBar() {
		this.floatBoundaryBar.hide();
		this.frozenBoundaryBar.hide();
		this.floatBoundaryBar = null;
		this.frozenBoundaryBar = null;
		
	}

	protected void releaseGhostTh() {
		this.floatGhostTh.hide();
		this.floatGhostTh = null;
	}

	//Private Methods

	private void setPopupProxyPosition(PopupPanel proxy,int absoluteLeft,int absoluteTop) {
		proxy.setPopupPosition(absoluteLeft,absoluteTop);
	}
	
	private void setCursorStyle(Event event) {
		if(atMostRight(event)){
			DOM.setStyleAttribute(this.getElement(), "cursor", "e-resize");
		}else{
			DOM.setStyleAttribute(this.getElement(), "cursor", "default");
		}
	}

	@SuppressWarnings("unused")
	private boolean atMostLeft(Event event) {
		int clientX=event.getClientX();
		int absoluteLeft=DOM.getAbsoluteLeft(this.getElement());
		if((clientX-absoluteLeft<EDGE_TOLERANCE)){
			return true;
		}else{
			return false;	
		}
	}
	
	private boolean atMostRight(Event event) {
		int absoluteMouseLeft=event.getClientX()+this.header.getTotalScrollLeft();
		int width=this.getElement().getOffsetWidth();
		int absoluteElementLeft=DOM.getAbsoluteLeft(this.getElement());
		if(width-(absoluteMouseLeft-absoluteElementLeft)<EDGE_TOLERANCE){
			return true;
		}else{
			return false;	
		}
	}
}
